<template>
  <div class="annotation-main" id="annotation">
    <div class="entity">
      <el-form
        :inline="true"
        class="entity-search km-form"
        @submit.native.prevent
      >
        <el-form-item>
          <el-input
            class="km-input"
            v-model="entitySearch"
            placeholder="标签搜索"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            class="km-button"
            @click="$refs.ontologyMenu.filter(entitySearch)"
            >搜索</el-button
          >
        </el-form-item>
      </el-form>
      <el-tree
        :data="ontologyMenu"
        @node-click="handleNodeClick"
        default-expand-all
        :expand-on-click-node="false"
        :filter-node-method="filterOntology"
        ref="ontologyMenu"
        class="entity-tree"
        ><span class="entity-item" slot-scope="{ node }">
          <span>{{ node.label }}</span>
        </span></el-tree
      >
    </div>
    <div class="article" id="article">
      <div class="article-tool-bar">
        <el-button
          @click="handleSave"
          :loading="saveLoading"
          class="km-button"
          size="small"
          ><i class="iconfont2 icon-baocun"></i>保存</el-button
        >
        <el-button
          v-if="type == 'annotation'"
          @click="handlePreAnnotation"
          class="km-button"
          size="small"
          ><i class="iconfont2 icon-biaoji"></i>预标注</el-button
        >
        <el-button
          v-if="type == 'proofread' && !isConfirmed"
          @click="handleConfirm"
          :loading="confirmLoading"
          class="km-button"
          size="small"
          ><i class="iconfont2 icon-queren"></i>确认</el-button
        >
        <el-button
          class="km-button"
          size="small"
          @click="() => (isFullscreen = !isFullscreen)"
        >
          <i
            class="iconfont2"
            :class="{
              'icon-full-screen': !isFullscreen,
              'icon-compress': isFullscreen,
            }"
          ></i>
          {{ isFullscreen ? "退出全屏" : "全屏显示" }}</el-button
        >
      </div>
      <div id="wangEditor-tool-bar"></div>
      <div class="article-annotation" ref="annotationBox">
        <div
          class="canvasBox"
          ref="canvasBox"
          @click.right.prevent="proxyRightClick"
        >
          <!-- 指向鼠标的连线 -->
          <svg
            id="relation-line"
            :width="relationLine.width + 'px'"
            :height="relationLine.height + 'px'"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            v-show="relationLine.show"
          >
            <line
              :x1="relationLine.targetX"
              :y1="relationLine.targetY"
              :x2="relationLine.pointerX"
              :y2="relationLine.pointerY"
              style="stroke: rgb(255, 0, 0); stroke-width: 1"
            />
            <circle
              :cx="relationLine.pointerX"
              :cy="relationLine.pointerY"
              r="2"
              fill="red"
            />
          </svg>
          <div id="wangEditor" @click="proxyClick"></div>
        </div>
      </div>
    </div>
    <div class="relationBlock">
      <el-form
        :inline="true"
        class="entity-search km-form"
        @submit.native.prevent
      >
        <el-form-item>
          <el-input
            class="km-input"
            v-model="relationSearch"
            placeholder="关系搜索"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" class="km-button" @click="onRelationSearch"
            >搜索</el-button
          >
        </el-form-item>
      </el-form>
      <div class="relation-tree">
        <div v-if="relationListInDom === null">没有关系</div>
        <div v-else>
          <div v-if="relationListInDom.length == 0">没有关系2</div>
          <div v-else class="relation-tree-list">
            <el-radio-group
              v-model="selectedCategoryId"
              class="relation-tree-list"
            >
              <el-radio
                border
                v-for="relation in relationListInDom"
                :label="relation.relationId"
                :key="relation.relationId"
                >{{ relation.relationShowName }}</el-radio
              >
            </el-radio-group>
          </div>
        </div>
      </div>
    </div>
    <ul
      v-show="remarkVisible"
      id="remarkmenu"
      class="contextMenu"
      :style="{
        left: remarkMenuLeft + 'px',
        top: remarkMenuTop + 'px',
      }"
    >
      <li v-show="rightClickTargetType == `entity`">
        <a href="javascript:;" @click="addProperty">标注属性</a>
      </li>
      <li>
        <a href="javascript:;" @click="onDeleteRemark">删除</a>
      </li>
    </ul>
    <ul
      v-if="propertyListVisible"
      class="contextMenu"
      id="propertyList"
      :style="{
        left: propertyListLeft + 'px',
        top: propertyListTop + 'px',
      }"
    >
      <li v-for="property in rightClickPropertyList" :key="property.propertyId">
        <a href="javascript:;" @click="selectProperty(property)">{{
          property.propertyName
        }}</a>
      </li>
    </ul>
  </div>
</template>
<script>
import wangEditor from "wangeditor";
// import MarkCanvas from "./canvas.js";
import MarkCanvas from "./svg.js";

export default {
  name: "Annotation",
  components: {},
  data: () => {
    return {
      saveLoading: false,
      preAnnotationLoading: false,
      confirmLoading: false,
      loading: false,
      editor: null,
      // 带有层级关系的实体名称列表
      ontologyMenu: [],
      // 不带层级关系的实体名称列表
      ontologyList: [],
      ontologyObj: {},
      // 搜索实体
      entitySearch: "",
      // 搜索关系
      relationSearch: "",

      relationList: null,
      relationListToShow: [],

      selectedCategoryId: null,
      // 实体右键菜单
      remarkVisible: false,
      // 属性标注列表可见性
      propertyListVisible: false,
      // 当前选中的实体属性列表
      rightClickpropertyList: [],
      // 当前选中的实体类型
      currentEntityType: null,

      rightClickTarget: null,
      rightClickTargetType: null,
      labelClickTarget: null,
      isMarkingAttribute: false,
      markCanvas: null,
      isFullscreen: false,
      // 随鼠标的连线
      relationLine: {
        show: false,
        targetY: 0,
        targetX: 0,
        topOffset: 0,
        leftOffset: 0,
        pointerX: 0,
        pointerY: 0,
        width: 0,
        height: 0,
      },
      remarkMenuLeft: 0,
      remarkMenuTop: 0,
      propertyListLeft: 0,
      propertyListTop: 0,
      colorList: [
        "#ABC4FF",
        "#F7C378",
        "#4FDADC",
        "#EE8FC4",
        "#C6ABFF",
        "#BFC6D3",

        "#D7B9B5",
        "#E3BABA",
        "#84D79F",
        "#B5C5B1",
        "#56CAED",
        "#C4B8A3",

        "#86BFFF",
        "#FF976F",
        "#96D6E5",
        "#D1BBD1",
        "#8BCFF2",
        "#DAB37E",

        "#F3B0CD",
        "#FFBE84",
        "#FF8181",
        "#ABABDF",
        "#FA99AC",
        "#FFC2AD",

        "#A1BFCE",
        "#C3DD8B",
        "#D1ACE0",
        "#83CDFF",
        "#FEA1BF",
        "#78DBBF",
      ],
    };
  },
  props: {
    type: {
      type: String,
      default: "annotation",
    },
    isConfirmed: {
      type: Boolean,
      default: false,
    },
    annotationSourceData: {
      type: Object,
      default: () => ({
        relationList: [],
        ontologyMenu: [],
        ontologyList: [],
        attributeList: [],
        content: "",
        record: {},
      }),
    },
  },
  computed: {},
  watch: {
    remarkVisible(value) {
      if (value) {
        document.body.addEventListener("click", this.closeRemarkMenu);
      } else {
        document.body.removeEventListener("click", this.closeRemarkMenu);
      }
    },
    propertyListVisible(value) {
      if (value) {
        setTimeout(() => {
          document.body.addEventListener("click", this.closePropertyListMenu);
        }, 100);
      } else {
        document.body.removeEventListener("click", this.closePropertyListMenu);
      }
    },
    // 全屏显示切换
    isFullscreen(value) {
      value
        ? document.getElementById("annotation").requestFullscreen()
        : document.fullscreenElement && document.exitFullscreen();
    },
  },
  created() {},
  mounted() {},
  computed: {
    relationListInDom() {
      return this.relationListToShow.filter((relation) => relation.isEnable);
    },
  },
  methods: {
    init() {
      this.ontologyMenu = [...this.annotationSourceData.ontologyMenu];

      this.ontologyList = this.annotationSourceData.ontologyList.map(
        (ontology, index) => {
          return {
            ...ontology,
            color: this.colorList[index % this.colorList.length],
          };
        }
      );

      this.ontologyList.forEach((ontology) => {
        this.ontologyObj[ontology.value] = ontology;
      });
      const relationList = this.annotationSourceData.relationList.map(
        (relation) => {
          const headEntity = this.ontologyObj[relation.headId];
          const tailEntity = this.ontologyObj[relation.tailId];
          const relationShowName = `${headEntity?.label} - ${relation.relationName} - ${tailEntity?.label}`;
          return { ...relation, relationShowName };
        }
      );
      this.relationList = [...relationList];
      this.relationListToShow = [...relationList];

      this.attributeList = this.ontologyList
        .filter((item) => item.propertyList)
        .map((item) =>
          item.propertyList.map((property) => ({
            ...property,
            color: item.color + "E6",
          }))
        )
        .flat();

      this.initEditor(this.annotationSourceData.content);

      this.restoreAnnotationData(this.annotationSourceData.record);
      document.getElementById("wangEditor").onmouseup = this.addPropertyMouseup;
      const editArea = document.querySelector(`[contenteditable="true"]`);
      if (editArea) {
        editArea.attributes.removeNamedItem("contenteditable");
      }
      // 过滤禁用的实体
      this.$nextTick(() => {
        this.$refs.ontologyMenu.filter("");
      });

      return true;
    },
    initEditor(content) {
      if (this.editor) {
        this.editor.destroy();
      }
      const editor = new wangEditor(`#wangEditor-tool-bar`, `#wangEditor`);

      // 隐藏menus
      editor.config.menus = [];
      // 隐藏placeholder
      editor.config.placeholder = "";
      // 设置编辑器高度
      editor.config.zIndex = 1000;
      // 创建编辑器
      editor.create();

      editor.txt.html(content);

      this.editor = editor;

      if (!this.canvasBox) {
        this.canvasBox = this.$refs.canvasBox;
        this.markCanvas = new MarkCanvas(
          this.canvasBox,
          [],
          this.onDelRelation
        );
      } else {
        this.markCanvas.clear();
      }

      // 通过其他方式退出全屏的监听
      document.getElementById("annotation").onfullscreenchange = () => {
        if (!document.fullscreenElement) {
          this.isFullscreen = false;
        }
      };
    },
    restoreAnnotationData(annotationData) {
      console.log(new Date().getTime());
      if (!annotationData) {
        return;
      }
      const { relationList, entityList, attributeList } = annotationData;

      let relationMapByTailId = {};
      relationList.forEach((relation) => {
        if (!relationMapByTailId[relation.tailEntityId]) {
          relationMapByTailId[relation.tailEntityId] = [];
        }
        relationMapByTailId[relation.tailEntityId].push(relation);
      });
      let entityMap = entityList.reduce((acc, entity) => {
        acc[entity.entityId] = entity;
        return acc;
      }, {});
      // 恢复实体标注
      entityList.forEach((entity) => {
        const target = document.querySelector(`[uuid="${entity.entityId}"]`);
        if (!target) {
          console.error("实体未找到", entity);
          return;
        }
        const ontology = this.ontologyObj[entity.entityTypeId];
        if (!ontology) {
          console.error("实体类别未找到", entity);
          return;
        }
        target.style.setProperty(
          "background-color",
          ontology.color,
          "important"
        );
        target.insertAdjacentHTML(
          "afterbegin",
          `<span
            class="ontology-type"
            id="${entity.entityId}"
            otid="${entity.entityTypeId}"
            >${ontology.label}</span>`
        );
      });
      // 恢复属性标注
      attributeList.forEach((attribute) => {
        const target = document.querySelector(
          `[uuid="${attribute.attributeId}"]`
        );
        if (!target) {
          console.error("属性未找到", attribute);
          return;
        }
        relationMapByTailId[attribute.attributeId];
        const relation = relationMapByTailId[attribute.attributeId]
          ? relationMapByTailId[attribute.attributeId][0]
          : null;
        const entity = entityMap[relation?.headEntityId];
        const entityType = this.ontologyObj[entity?.entityTypeId];
        target.style.setProperty(
          "background-color",
          entityType.color + "CC",
          "important"
        );

        // 抽取产生的记录 没有 attributeTypeId
        if (!attribute.attributeTypeId) {
          target.insertAdjacentHTML(
            "afterbegin",
            `<span
              class="ontology-type"
              id="${attribute.attributeId}"
              attributeType="${attribute.attributeType}"
              >${attribute.attributeType}</span>`
          );
          // 标注记录 有attributeTypeId
        } else {
          const attributeType = this.attributeList.find(
            (attributeType) =>
              attributeType.propertyId == attribute.attributeTypeId
          );

          if (!attributeType) {
            console.error("属性类型未找到", attribute);
            return;
          }

          target.style.setProperty(
            "background-color",
            attributeType.color,
            "important"
          );
          target.insertAdjacentHTML(
            "afterbegin",
            `<span
              class="ontology-type"
              id="${attribute.attributeId}"
              otid="${attribute.attributeTypeId}"
              >${attributeType.propertyName}</span>`
          );
        }
      });

      let relationListForCanvas = [];

      // 恢复关系标注
      relationList.forEach((relation) => {
        let relationType = this.relationList.find(
          (relationType) => relation.relationTypeId == relationType.relationId
        );
        // 属性关系的信息
        if (relation.relationTypeId == "0") {
          relationType = {
            relationId: "0",
            relationName: "属性",
          };
        }
        if (!relationType) {
          console.error("关系恢复失败 未找到关系类型", relation);
          return;
        }
        try {
          let MarkRelationInfo = this.getMarkRelationInfo(
            document.getElementById(relation.headEntityId),
            document.getElementById(relation.tailEntityId),
            relationType
          );
          relationListForCanvas.push(MarkRelationInfo);
        } catch (e) {
          console.error("关系恢复失败 生成关系信息失败", e, relation);
        }
      });
      try {
        this.markCanvas.cacheElements = relationListForCanvas;
        this.markCanvas.initRelations();
      } catch (e) {
        console.error("关系恢复失败 关系连线绘制失败", e);
      }
      console.log(new Date().getTime());
    },

    handleNodeClick(data) {
      const entityType = this.ontologyObj[data.value];
      this.currentEntityType = {
        propertyId: data.value,
        propertyName: data.label,
        color: entityType.color,
      };
    },
    addPropertyMouseup(event) {
      if (this.isMarkingAttribute) {
        this.markProperty(event);
      } else {
        this.markEntity(this.currentEntityType, "entity");
      }
    },
    markProperty(event) {
      // 如果没有选择文本 则不弹出属性选择框
      const $selectionText = window.getSelection().toString();
      if (!$selectionText) {
        return;
      }
      this.isMarkingAttribute = false;
      this.propertyListVisible = true;
      this.propertyListLeft = event.pageX - 240;
      this.propertyListTop = event.pageY;
    },
    selectProperty(property) {
      const { id: uuid } = this.markEntity(property, "attribute");
      this.markRelation(this.rightClickTarget, document.getElementById(uuid), {
        relationId: 0,
        relationName: "属性",
      });
      this.rightClickTarget = null;
    },
    // 标注实体或属性
    markEntity(property, type) {
      const windowSelection = window.getSelection();
      const $selectionText = windowSelection.toString();
      if (!$selectionText) {
        return;
      }
      if (!property || !property.propertyName || !property.propertyId) {
        this.$message.warning("请先选中实体类别");
        return;
      }

      const { anchorNode, anchorOffset, focusNode, focusOffset } =
        windowSelection;

      const [startIndex, endIndex] =
        anchorOffset > focusOffset
          ? [focusOffset, anchorOffset]
          : [anchorOffset, focusOffset];

      if (anchorNode.parentNode.classList.contains("ontology")) {
        this.$message.warning("当前选中文本无法被标注");
        console.log("嵌套标注");
        return;
      }

      if (anchorNode.parentNode.classList.contains("ontology-type")) {
        this.$message.warning("当前选中文本无法被标注");
        console.log("标注实体类别");
        return;
      }

      if (focusNode !== anchorNode) {
        this.$message.warning("当前选中文本无法被标注");
        console.log("跨节点");
        return false;
      }

      const uuid = guid();

      const newDiv = document.createElement("span");

      newDiv.innerHTML = `<span class="ontology" type="${type}" uuid="${uuid}">${$selectionText}</span>`;

      const fragment = document.createDocumentFragment();
      fragment.append(
        anchorNode.nodeValue.slice(0, startIndex),
        newDiv.firstElementChild,
        anchorNode.nodeValue.slice(endIndex)
      );
      anchorNode.parentNode.replaceChild(fragment, anchorNode);

      const target = document.querySelector(`[uuid="${uuid}"]`);

      if (!target) {
        this.$message.warning("当前选中文本无法标注");
        console.log("替换失败，未找到替换后的dom");
        return false;
      }

      target.style.setProperty("background-color", property.color, "important");
      target.insertAdjacentHTML(
        "afterbegin",
        `<span
          class="ontology-type"
          id="${uuid}"
          otid="${property.propertyId}"
          >${property.propertyName}</span>`
      );

      // 标注之后重绘连线

      this.markCanvas.initRelations();

      return {
        id: uuid,
        property,
      };
    },
    // 关闭右键菜单
    closeRemarkMenu() {
      this.remarkVisible = false;
    },
    closePropertyListMenu() {
      this.propertyListVisible = false;
    },
    proxyRightClick(e) {
      if (e.target.classList.contains("ontology-type")) {
        if (e.target.parentNode.attributes?.type?.value !== "entity") {
          this.rightClickTargetType = "attribute";
        } else {
          this.rightClickTargetType = "entity";
        }
        this.remarkVisible = true;
        this.remarkMenuLeft = e.pageX - 240;
        this.remarkMenuTop = e.pageY;
        this.rightClickTarget = e.target;
        return;
      }
      if (e.target.classList.contains("relationLabel")) {
        this.rightClickTargetType = "relation";
        this.remarkVisible = true;
        this.remarkMenuLeft = e.pageX - 240;
        this.remarkMenuTop = e.pageY;
        this.rightClickTarget = e.target;
      }
    },
    proxyClick(e) {
      if (e.target.classList.contains("ontology-type")) {
        if (this.labelClickTarget) {
          this.hideRelationLine();
          let relation;
          if (this.selectedCategoryId) {
            relation = this.relationList.find(
              (relation) => this.selectedCategoryId == relation.relationId
            );
          } else {
            this.$message.warning("请先选择关系");
            this.labelClickTarget = null;
            return false;
          }

          // 如果是属性关系 则不能标注关系
          if (
            this.labelClickTarget.parentNode.attributes?.type?.value !==
              "entity" ||
            e.target.parentNode.attributes?.type?.value !== "entity"
          ) {
            this.$message.warning("不能为属性标注关系");
            this.labelClickTarget = null;
            return false;
          }

          // 如果实体间不存在该关系 则不能标注关系
          if (
            this.labelClickTarget?.attributes?.otid?.value != relation.headId ||
            e.target?.attributes?.otid?.value != relation.tailId
          ) {
            this.$message.warning(
              `当前实体不存在关系 "${relation.relationShowName}"`
            );
            this.labelClickTarget = null;
            return false;
          }

          this.markRelation(this.labelClickTarget, e.target, relation);
          this.labelClickTarget = null;
        } else {
          this.showRelationLine(e.target);
          this.labelClickTarget = e.target;
        }
      } else {
        this.labelClickTarget = null;
        this.hideRelationLine();
      }
    },
    // 获取关系标注信息
    getMarkRelationInfo(startElement, endElement, relation) {
      const uperElement = this.getUperOne(startElement, endElement);
      let currentMarginTop = uperElement.parentNode.style.marginTop;

      if (currentMarginTop) {
        currentMarginTop = parseInt(currentMarginTop);
      }
      uperElement.parentNode.style.marginTop = currentMarginTop + 30 + "px";
      return {
        id: guid(),
        startElement: startElement.id,
        endElement: endElement.id,
        relation: relation.relationName,
        relationTypeId: relation.relationId,
        step: currentMarginTop + 20,
      };
    },
    // 标注关系方法
    markRelation(startElement, endElement, relation) {
      this.markCanvas.createRelation(
        this.getMarkRelationInfo(startElement, endElement, relation)
      );
    },
    getUperOne(element1, element2) {
      const { top: top1, left: left1 } = this.getElementOffset(element1);
      const { top: top2, left: left2 } = this.getElementOffset(element2);
      if (top1 !== top2) {
        return top1 < top2 ? element1 : element2;
      } else {
        return left1 < left2 ? element1 : element2;
      }
    },
    getElementOffset(el) {
      let top = 0;
      let left = 0;
      do {
        top += el.offsetTop;
        left += el.offsetLeft;
        el = el.offsetParent;
      } while (el);

      return { top, left };
    },
    // 展示标注随鼠标的连线
    showRelationLine(e) {
      let box = document.getElementById("wangEditor");

      const { top: targetY, left: targetX } = this.getElementOffset(e);
      const { top, left } = this.getElementOffset(
        document.getElementById("wangEditor")
      );

      // 起始点坐标
      this.relationLine.targetX = targetX - left;
      this.relationLine.targetY = targetY - top;

      // 画布大小
      this.relationLine.height = box.scrollHeight;
      this.relationLine.width = box.scrollWidth;

      // 终点初始坐标防止初始显示时连线漂移
      this.relationLine.pointerX = targetX - left;
      this.relationLine.pointerY = targetY - top;

      this.relationLine.leftOffset = left;
      this.relationLine.topOffset = top;

      // 展示连线
      this.relationLine.show = true;

      document.addEventListener("mousemove", this.followMouse);
    },
    // 鼠标移动更新连线终点
    followMouse(e) {
      this.relationLine.pointerX =
        e.pageX -
        this.relationLine.leftOffset +
        this.$refs?.annotationBox?.scrollLeft;
      this.relationLine.pointerY =
        e.pageY -
        this.relationLine.topOffset +
        this.$refs?.annotationBox?.scrollTop;
    },

    // 隐藏连线
    hideRelationLine() {
      this.relationLine.show = false;
      document.removeEventListener("mousemove", this.followMouse);
    },
    addProperty() {
      const entityTypeId = this.rightClickTarget.attributes.otid.value;
      const ontology = this.ontologyObj[entityTypeId];
      if (!(ontology && ontology.propertyList)) {
        this.$message.warning("当前实体没有定义属性");
        return;
      }
      this.rightClickPropertyList = ontology.propertyList.map((item) => ({
        ...item,
        // 属性背景色透明度80%
        color: ontology.color + "CC",
      }));
      this.isMarkingAttribute = true;
    },
    onDeleteRemark() {
      if (this.rightClickTargetType == "relation") {
        this.markCanvas.deleteRelation(this.rightClickTarget.id);
      } else {
        this.deleteOntology(this.rightClickTarget);
      }
      this.rightClickTarget = null;
    },
    deleteOntology(node) {
      const { parentNode, id } = node;
      // 删除实体上的属性及关系
      // 浅克隆防止循环内操作数组后循环指针位置错乱
      [...this.markCanvas.cacheElements].forEach((relation) => {
        if (id == relation.startElement || id == relation.endElement) {
          this.markCanvas.deleteRelation(relation.id);
        }
      });

      // 尝试移除实体类别标签节点
      // 删除属性时，会触发删除属性关系中删除尾实体的回调导致重复删除报错
      try {
        // 删除实体类别标签节点
        parentNode.removeChild(node);
        // 将被标注的实体span转为普通的text
        const grandpaNode = parentNode.parentNode;
        grandpaNode.innerHTML = grandpaNode.innerHTML.replace(
          parentNode.outerHTML,
          parentNode.textContent
        );
        // 重绘关系连线
        this.markCanvas.initRelations();
      } catch (error) {}
    },

    // 关系删除回调函数
    onDelRelation(relation) {
      const { step } = relation;
      const startElement = document.getElementById(relation.startElement);
      const endElement = document.getElementById(relation.endElement);
      if (!startElement || !endElement) {
        console.log("startElement:", startElement, "endElement:", endElement);
        return;
      }
      const uperElement = this.getUperOne(startElement, endElement);

      // 遍历关系列表 找出有对应影响的关系 降低其step高度
      this.markCanvas.cacheElements.forEach((relation) => {
        const startElement = document.getElementById(relation.startElement);
        const endElement = document.getElementById(relation.endElement);
        if (!startElement || !endElement) {
          console.log("startElement:", startElement, "endElement:", endElement);
          return;
        }
        const currentUperElement = this.getUperOne(startElement, endElement);
        if (currentUperElement.id == uperElement.id) {
          // 判断是否应当减小当前连线高度
          if (relation.step > step) {
            relation.step -= 30;
          }
        }
      });

      let currentMarginTop = uperElement.parentNode.style.marginTop;

      if (currentMarginTop) {
        currentMarginTop = parseInt(currentMarginTop);
      }
      if (currentMarginTop >= 30) {
        uperElement.parentNode.style.marginTop = currentMarginTop - 30 + "px";
      } else {
        uperElement.parentNode.style.marginTop = 0;
      }

      // 重绘关系
      this.markCanvas.initRelations();

      // 当关系类型为属性时删除对应尾实体
      if (relation.relationTypeId == "0") {
        this.deleteOntology(endElement);
      }
    },

    async handlePreAnnotation() {
      const entity = document.querySelector(`.ontology-type`);
      if (entity) {
        try {
          await this.$confirm(
            "是否确认进行预标注？预标注后将会覆盖当前已标注内容。",
            "预标注",
            {
              confirmButtonText: "确定",
              cancelButtonText: "取消",
              type: "warning",
              customClass: "pre-annotation-confirm",
            }
          );
        } catch (e) {
          console.log(e);
          return;
        }
      }
      this.$emit("preAnnotation", "", () => {
        this.preAnnotationLoading = false;
      });
    },

    // 保存标注数据
    handleSave() {
      this.saveLoading = true;
      const relationList = this.getRelationList();
      const { entityList, attributeList } = this.getEntityList();
      const article = this.getArticle();
      const params = {
        relationList,
        entityList,
        attributeList,
        article,
      };
      console.log(params);
      this.$emit("save", params, () => {
        this.saveLoading = false;
      });
    },

    // 确认抽取数据
    handleConfirm() {
      this.confirmLoading = true;
      const relationList = this.getRelationList();
      const { entityList, attributeList } = this.getEntityList();
      const article = this.getArticle();
      const params = {
        relationList,
        entityList,
        attributeList,
        article,
      };
      console.log(params);
      this.$emit("confirm", params, () => {
        this.confirmLoading = false;
      });
    },

    // 从关系标注数据中获取保存用的数据结构
    getRelationList() {
      const relationList = this.markCanvas.cacheElements.map((relation) => ({
        id: relation.id,
        headEntityId: relation.startElement,
        tailEntityId: relation.endElement,
        relationTypeId: relation.relationTypeId.toString(),
      }));
      return relationList;
    },
    // 从实体标注数据中获取保存用的数据结构 包含实体和属性
    getEntityList() {
      let entityList = [];
      let attributeList = [];
      document.querySelectorAll(".ontology-type").forEach((entityElement) => {
        // 属性实体
        if (entityElement.parentNode.attributes.type.value == "attribute") {
          // 具有id的属性实体
          if (entityElement.attributes.otid && entityElement.attributes.otid) {
            attributeList.push({
              attributeTypeId: entityElement.attributes.otid.value,
              attributeValue:
                entityElement.parentNode.childNodes[
                  entityElement.parentNode.childNodes.length - 1
                ].textContent,
              attributeId: entityElement.id,
            });
            // 没有id的属性实体 私有属性
          } else {
            attributeList.push({
              attributeType: entityElement.attributes.attributeType.value,
              attributeValue:
                entityElement.parentNode.childNodes[
                  entityElement.parentNode.childNodes.length - 1
                ].textContent,
              attributeId: entityElement.id,
            });
          }
          // 普通实体
        } else {
          entityList.push({
            entityTypeId: entityElement.attributes.otid.value,
            entityName:
              entityElement.parentNode.childNodes[
                entityElement.parentNode.childNodes.length - 1
              ].textContent,
            entityId: entityElement.id,
          });
        }
      });
      return { entityList, attributeList };
    },
    // 获取文章html并进行内容处理
    getArticle() {
      // 获取文章内容并移除标注的实体类别标
      let article = this.editor.txt
        .html()
        // 移除默认空行
        .replace("<p><br/></p>", "")
        // 移除标注的实体类别及属性标签
        .replace(/<span class="ontology-type" .*?<\/span>/gi, "")
        // 移除实体标签的style
        .replace(
          /(<span class="ontology" [\s\S]*?) style="[\s\S]*?"(>[\s\S]*?<\/span>)/gi,
          "$1$2"
        );

      return article;
    },
    filterOntology(value, data) {
      if (!data.isEnable) {
        return false;
      }
      if (!value) {
        return true;
      }
      return data.label.indexOf(value) !== -1;
    },
    onRelationSearch() {
      if (!this.relationSearch) {
        this.relationListToShow = this.relationList;
        return;
      }
      this.relationListToShow = this.relationList.filter(
        (relation) =>
          relation.relationShowName.indexOf(this.relationSearch) !== -1
      );
      this.selectedCategoryId = null;
    },
  },
  beforeDestroy() {
    document.getElementById("wangEditor")
      ? (document.getElementById("wangEditor").onmouseup = null)
      : "";
    const editor = this.editor;
    if (editor == null) return;
    // 销毁，并移除 editor
    editor.destroy();
    document.removeEventListener("mousemove", this.followMouse);
    document.body.removeEventListener("click", this.closePropertyListMenu);
    document.body.removeEventListener("click", this.closeRemarkMenu);
  },
};
function guid() {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
    let r = (Math.random() * 16) | 0,
      v = c == "x" ? r : (r & 0x3) | 0x8;

    return v.toString(16);
  });
}
</script>
<style lang="scss" scoped>
.annotation-main {
  display: flex;
  justify-content: space-between;
  height: calc(100vh - 120px);
  width: 100%;
  background-color: #fff;
  border-radius: 4px;
  &:fullscreen {
    height: 100%;
    padding: 30px;
  }
  .entity {
    flex-shrink: 0;

    width: 280px;
    height: 100%;
    .entity-tree {
      height: calc(100% - 62px);
      overflow: auto;
      &::-webkit-scrollbar {
        display: none; /* Chrome Safari */
      }
      ::v-deep .el-tree-node {
        margin-top: 12px;
        .el-tree-node__content {
          height: auto;
        }
        &:focus > .el-tree-node__content {
          background-color: #fff;
        }
        &.is-current {
          > .el-tree-node__content {
            .entity-item {
              background: rgba(75, 113, 235, 0.12);
              border: 1px solid #4b71eb;
              color: #4b71eb;
            }
          }
        }
      }
      .entity-item {
        padding: 0 16px;
        height: 32px;
        border-radius: 2px;
        border: 1px solid #dddddd;
        line-height: 32px;
        font-size: 14px;
        color: #333333;
      }
      &.el-tree {
        ::v-deep .el-tree-node__expand-icon {
          font-family: "iconfont2" !important;
          font-size: 14px;
          font-style: normal;
          -webkit-font-smoothing: antialiased;
          -moz-osx-font-smoothing: grayscale;
        }
        ::v-deep .el-icon-caret-right:before {
          // 未展开
          content: "\e65b";
        }
        ::v-deep .expanded:before {
          content: "\e65a";
          // 展开
        }
        ::v-deep .expanded {
          // 展开不旋转
          -webkit-transform: rotate(0deg);
          transform: rotate(0deg);
        }
      }
    }
  }
  .article {
    flex-grow: 1;
    width: 100%;
    overflow: auto;
    border: 1px solid #dddddd;
    box-shadow: inset 0px 0px 0px 16px #f3f4f8;
    padding: 16px;
    border-radius: 4px;
    ::v-deep .w-e-text-container {
      background-color: rgba(0, 0, 0, 0);
    }
    .article-tool-bar {
      margin: 10px 20px;
      display: flex;
      justify-content: flex-end;
      .km-button {
        border-color: $--color-primary;
        color: $--color-primary;
        .iconfont2 {
          font-size: 12px;
          margin-right: 8px;
        }
      }
    }
    > .article-annotation {
      height: calc(100% - 52px);
      overflow: overlay;

      &::-webkit-scrollbar-track-piece {
        background: #d3dce600;
      }

      &::-webkit-scrollbar {
        width: 6px;
        height: 6px;
      }

      &::-webkit-scrollbar-thumb {
        background: #b7bbc700;
        border-radius: 20px;
      }
      &:hover::-webkit-scrollbar-thumb {
        background: #b7bbc7;
      }
    }
    &::-webkit-scrollbar {
      display: none; /* Chrome Safari */
    }
  }
  .relationBlock {
    flex-shrink: 0;
    width: 280px;
    margin-left: 24px;
    .relation-tree {
      height: calc(100% - 62px);
      overflow: hidden auto;

      &::-webkit-scrollbar {
        display: none; /* Chrome Safari */
      }
      .relation-tree-list {
        display: flex;
        flex-direction: column;
      }
      .el-radio.is-bordered,
      .el-radio.is-bordered + .el-radio.is-bordered {
        margin: 10px 20px 0px 0px;
      }
    }
    .el-tag {
      margin: 10px;
    }
    overflow: auto;
  }

  .contextMenu {
    position: absolute;
    border: 1px solid steelblue;
    width: 100px;
    height: auto;
    padding: 3px;
    border-radius: 3px;
    background-color: #fff;
    z-index: 99999;
    li {
      list-style: none;
      width: 100%;
      height: 24px;
      line-height: 24px;
      font-size: 14px;
      display: inline-block;
      padding: 0;
      margin: 0 0 3px 0px;
      a {
        display: flex;
        align-items: center;
        justify-content: center;
        text-decoration: none;
        color: black;
        &:hover {
          background-color: rgba(77, 112, 190, 0.1);
          color: white;
        }

        &:active {
          background-color: rgba(77, 112, 190, 1);
        }
      }
    }
  }
  #relation-line {
    position: absolute;
  }

  ::v-deep .canvasBox {
    position: relative;
    display: inline-block;
    #wangEditor {
      width: 100%;
      height: 100%;
      color: #000;
      .multiple-ontology {
        display: inline-flex;
        flex-direction: column;
        vertical-align: bottom;
        border-radius: 2px;
        border: 1px solid #1e253d;
        padding: 0 3px 3px 3px;
        .ontology {
          margin-top: 3px;
        }
      }
      .ontology {
        display: inline-flex;
        flex-wrap: wrap;
        flex-direction: column;
        align-items: center;
        vertical-align: bottom;
        border-radius: 2px;
        text-indent: 0px;
        text-align: center;
        padding: 2px 2px 0 2px;
        // &[type="attribute"] {
        //   .ontology-type {
        //     background-color: #c2fcfc !important;
        //   }
        // }
        .ontology-type {
          border-radius: 2px;
          cursor: pointer;
          margin-bottom: 2px;
          padding: 3px;
          background-color: #fff !important;
          // font-size: 50%;
          width: 100%;
        }
      }
      div,
      p,
      span,
      strong {
        background-color: transparent !important;
      }
    }
    canvas {
      position: absolute;
      top: 0;
      left: 0;
    }
    .relationLabel {
      color: #000;
      background: #fff;
      z-index: 2;
      max-width: 100px;
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;
      border-radius: 5px;
      padding: 1px;
    }
  }
  .km-form.el-form {
    .el-form-item {
      margin-right: 0px;
      .km-input {
        width: 180px;
        height: 32px;
        background: #ffffff;
        line-height: 32px;
        ::v-deep .el-input__inner {
          border: 1px solid #dddddd;
          border-radius: 2px 0px 0px 2px;
          height: 32px;
          line-height: 32px;
        }
      }
      .km-button {
        border-radius: 0px 2px 2px 0px;
        width: 60px;
        height: 32px;
        padding: 0;
      }
      ::v-deep .el-form-item__content {
        line-height: 32px;
      }
    }
  }
}
</style>
